class ToolDraw {
  static readonly minScale: number = 20;
  static readonly maxScale: number = 60;
  static readonly defaultScale: number = 40;
  static readonly scaleStep: number = 10;
  static readonly line: number = 6;
  static readonly funcStep: number = 0.1;
  static readonly fontSize: number = 12;

  static app: CanvasRenderingContext2D;
  static w: number;
  static h: number;
  static scale: number = 40;

  public offsetX: number = 20;
  public offsetY: number = 20;
  public express: string = "";
  public funcScale: number = 1;
  public funcXStart: number = 0;
  public funcXEnd: number = 0;
  public moveTimes: moveTimes = {
    vertical: 0,
    level: 0,
  };

  constructor(
    public el: HTMLCanvasElement,
    private limits: Limits,
    private colors?: Colors
  ) {
    if (this.limits === undefined) {
      this.el.width = 400;
      this.el.height = 400;
    } else {
      this.el.width = this.limits.width;
      this.el.height = this.limits.height;
    }

    ToolDraw.w = this.el.width;
    ToolDraw.h = this.el.height;
    ToolDraw.app = this.el.getContext("2d")!;

    if (this.colors === undefined) {
      this.colors = {};
    }

    this.colors!.axisColor = colors?.axisColor || "#000";
    this.colors!.functionColor = colors?.functionColor || "red";
    this.colors!.gridColor = colors?.gridColor || "#ddd";
    this.colors!.textColor = colors?.textColor || "green";
    this.colors!.positionColor = colors?.positionColor || "#666";

    this.initAxis(ToolDraw.w / 2, ToolDraw.h / 2);
  }

  drawGrid(color: string) {
    let x, y;

    ToolDraw.app.save();
    ToolDraw.app.lineWidth = 1;
    ToolDraw.app.strokeStyle = color;
    ToolDraw.app.beginPath();

    y = ToolDraw.scale;

    while (y <= ToolDraw.h / 2 - this.offsetY * this.moveTimes.vertical) {
      ToolDraw.app.moveTo(
        -ToolDraw.w / 2 - this.offsetX * this.moveTimes.level,
        y
      );
      ToolDraw.app.lineTo(
        ToolDraw.w / 2 - this.offsetX * this.moveTimes.level,
        y
      );

      ToolDraw.app.stroke();
      y += ToolDraw.scale;
    }

    y = -ToolDraw.scale;

    while (y >= -ToolDraw.h / 2 - this.offsetY * this.moveTimes.vertical) {
      ToolDraw.app.moveTo(
        -ToolDraw.w / 2 - this.offsetX * this.moveTimes.level,
        y
      );
      ToolDraw.app.lineTo(
        ToolDraw.w / 2 - this.offsetX * this.moveTimes.level,
        y
      );
      ToolDraw.app.stroke();
      y -= ToolDraw.scale;
    }

    x = ToolDraw.scale;

    while (x <= ToolDraw.w / 2 - this.offsetX * this.moveTimes.level) {
      ToolDraw.app.moveTo(
        x,
        -ToolDraw.h / 2 - this.offsetY * this.moveTimes.vertical
      );
      ToolDraw.app.lineTo(
        x,
        ToolDraw.h / 2 - this.offsetY * this.moveTimes.vertical
      );
      ToolDraw.app.stroke();
      x += ToolDraw.scale;
    }

    x = -ToolDraw.scale;

    while (x >= -ToolDraw.w / 2 - this.offsetX * this.moveTimes.level) {
      ToolDraw.app.moveTo(
        x,
        -ToolDraw.h / 2 - this.offsetY * this.moveTimes.vertical
      );
      ToolDraw.app.lineTo(
        x,
        ToolDraw.h / 2 - this.offsetY * this.moveTimes.vertical
      );
      ToolDraw.app.stroke();
      x -= ToolDraw.scale;
    }

    ToolDraw.app.closePath();
    ToolDraw.app.restore();
  }

  drawAll() {
    this.drawGrid(this.colors!.gridColor!);
    this.drawAxis(this.colors!.axisColor!);
    this.drawTextAndPosition(
      this.colors!.textColor!,
      this.colors!.positionColor!
    );
    this.drawFunction(this.colors!.functionColor!);
  }

  clear() {
    ToolDraw.app.clearRect(
      -ToolDraw.w / 2 - this.offsetX * this.moveTimes.level,
      -ToolDraw.h / 2 - this.offsetY * this.moveTimes.vertical,
      ToolDraw.w + Math.abs(this.offsetX * this.moveTimes.level),
      ToolDraw.h + Math.abs(this.offsetY * this.moveTimes.vertical)
    );
  }

  zoomIn() {
    if (ToolDraw.scale < ToolDraw.maxScale) {
      ToolDraw.scale += 10;
    } else {
      ToolDraw.scale = ToolDraw.defaultScale;
      this.funcScale *= 2;
    }
  }

  zoomOut() {
    if (ToolDraw.scale > ToolDraw.minScale) {
      ToolDraw.scale -= 10;
    } else {
      ToolDraw.scale = ToolDraw.defaultScale;
      this.funcScale /= 2;
    }
  }

  backAxis() {
    if (this.moveTimes.level > 0) {
      this.initAxis(-this.offsetX * this.moveTimes.level, 0);
      ToolDraw.app.clearRect(
        -ToolDraw.w / 2,
        -ToolDraw.h / 2,
        ToolDraw.w,
        ToolDraw.h
      );
      this.moveTimes.level = 0;
    } else if (this.moveTimes.level < 0) {
      this.initAxis(this.offsetX * Math.abs(this.moveTimes.level), 0);
      ToolDraw.app.clearRect(
        -ToolDraw.w / 2,
        -ToolDraw.h / 2,
        ToolDraw.w,
        ToolDraw.h
      );
      this.moveTimes.level = 0;
    }
    if (this.moveTimes.vertical > 0) {
      this.initAxis(0, -this.offsetY * this.moveTimes.vertical);
      ToolDraw.app.clearRect(
        -ToolDraw.w / 2,
        -ToolDraw.h / 2,
        ToolDraw.w,
        ToolDraw.h
      );
      this.moveTimes.vertical = 0;
    } else if (this.moveTimes.vertical < 0) {
      this.initAxis(0, this.offsetY * Math.abs(this.moveTimes.vertical));
      ToolDraw.app.clearRect(
        -ToolDraw.w / 2,
        -ToolDraw.h / 2,
        ToolDraw.w,
        ToolDraw.h
      );
      this.moveTimes.vertical = 0;
    }
    this.drawAll();
  }

  initAxis(offsetX: number, offsetY: number) {
    ToolDraw.app.translate(offsetX, offsetY);
  }

  drawFunction(color: string) {
    let x, y;
    ToolDraw.app.save();
    ToolDraw.app.strokeStyle = color;
    ToolDraw.app.beginPath();

    x = this.funcXStart;

    while (x < this.funcXEnd) {
      ToolDraw.app.moveTo(
        (x * ToolDraw.scale) / this.funcScale,
        (-eval(this.express) * ToolDraw.scale) / this.funcScale
      );

      x += ToolDraw.funcStep;
      ToolDraw.app.lineTo(
        (x * ToolDraw.scale) / this.funcScale,
        (-eval(this.express) * ToolDraw.scale) / this.funcScale
      );
    }
    ToolDraw.app.stroke();

    ToolDraw.app.closePath();
    ToolDraw.app.restore();
  }
  toChangeText() {
    
  }
  drawTextAndPosition(textColor: string, lineColor: string) {
    let x, y;
    let text;

    ToolDraw.app.save();
    ToolDraw.app.beginPath();
    ToolDraw.app.fillStyle = textColor;
    ToolDraw.app.strokeStyle = lineColor;
    ToolDraw.app.font = `${ToolDraw.fontSize}px serif`;

    text = -this.funcScale;
    y = ToolDraw.scale;

    while (y <= ToolDraw.h / 2 - this.offsetY * this.moveTimes.vertical) {
      ToolDraw.app.fillText(`${text}`, ToolDraw.fontSize / 2, y);
      ToolDraw.app.moveTo(-ToolDraw.line / 2, y);
      ToolDraw.app.lineTo(ToolDraw.line / 2, y);

      y += ToolDraw.scale;
      text -= this.funcScale;
    }

    text = this.funcScale;
    y = -ToolDraw.scale;

    while (y >= -ToolDraw.h / 2 - this.offsetY * this.moveTimes.vertical) {
      ToolDraw.app.fillText(`+${text}`, ToolDraw.fontSize / 2, y);
      ToolDraw.app.moveTo(-ToolDraw.line / 2, y);
      ToolDraw.app.lineTo(ToolDraw.line / 2, y);

      y -= ToolDraw.scale;
      text += this.funcScale;
    }

    text = -this.funcScale;
    x = -ToolDraw.scale;

    while (x >= -ToolDraw.w / 2 - this.offsetX * this.moveTimes.level) {
      ToolDraw.app.fillText(`${text}`, x, ToolDraw.fontSize);
      ToolDraw.app.moveTo(x, -ToolDraw.line / 2);
      ToolDraw.app.lineTo(x, ToolDraw.line / 2);

      x -= ToolDraw.scale;
      text -= this.funcScale;
    }

    text = this.funcScale;
    x = ToolDraw.scale;

    while (x <= ToolDraw.w / 2 - this.offsetX * this.moveTimes.level) {
      ToolDraw.app.fillText(`+${text}`, x, ToolDraw.fontSize);
      ToolDraw.app.moveTo(x, -ToolDraw.line / 2);
      ToolDraw.app.lineTo(x, ToolDraw.line / 2);

      x += ToolDraw.scale;
      text += this.funcScale;
    }

    ToolDraw.app.stroke();
    ToolDraw.app.closePath();
    ToolDraw.app.restore();
  }

  drawAxis(color: string) {
    ToolDraw.app.save();
    ToolDraw.app.strokeStyle = color;
    ToolDraw.app.beginPath();
    ToolDraw.app.moveTo(
      -ToolDraw.w / 2 - this.offsetX * this.moveTimes.level,
      0
    );
    ToolDraw.app.lineTo(
      ToolDraw.w / 2 - this.offsetX * this.moveTimes.level,
      0
    );
    ToolDraw.app.moveTo(
      0,
      -ToolDraw.h / 2 - this.offsetY * this.moveTimes.vertical
    );
    ToolDraw.app.lineTo(
      0,
      ToolDraw.h / 2 - this.offsetY * this.moveTimes.vertical
    );
    ToolDraw.app.stroke();
    ToolDraw.app.closePath();
    ToolDraw.app.restore();
  }
}

export default ToolDraw;
